/*
 * ResXMLTreeNode.cpp
 *
 *  Created on: May 26, 2018
 *      Author: hp
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "android-xml.h"
#include "ResXMLTreeNode.h"
#include "XMLNameSpace.h"

ResXMLTreeNode::ResXMLTreeNode(bool root) :
		_root(root) {
}

ResXMLTreeNode::ResXMLTreeNode(const ResStringPool& string_pool,
		const char* data) {
	ResXMLTree_node header;
	memcpy(&header, data, sizeof(ResChunk_header));

	ResXMLTree_attrExt attrs;
	memcpy(&attrs, data + header.header.headerSize, sizeof(ResXMLTree_attrExt));

	printf("AttributeStart: %i\n", attrs.attributeStart);
	printf("AttributeSize: %i\n", attrs.attributeSize);
	printf("AttributeCount: %i\n", attrs.attributeCount);

	_name = string_pool.get(attrs.name.index);
	_ns = string_pool.get(attrs.ns.index);

	for (int i = 0; i < attrs.attributeCount; ++i) {
		ResXMLTree_attribute attribute;
		memcpy(&attribute,
				data + header.header.headerSize + sizeof(ResXMLTree_attrExt)
						+ (i * attrs.attributeSize),
				sizeof(ResXMLTree_attribute));

		ResXMLTreeNodeAttribute* parsed_attribute = new ResXMLTreeNodeAttribute;
		parsed_attribute->ns = string_pool.get(attribute.ns.index);
		parsed_attribute->name = string_pool.get(attribute.name.index);
		parsed_attribute->rawValue = string_pool.get(attribute.rawValue.index);
		parsed_attribute->typedValue = ResValue(string_pool,
				attribute.typedValue);

		add_attribute(parsed_attribute);
	}
}

void ResXMLTreeNode::add_attribute(ResXMLTreeNodeAttribute* attribute) {
	++_num_attributes;
	_attributes = (ResXMLTreeNodeAttribute**) realloc(_attributes,
			_num_attributes * sizeof(ResXMLTreeNodeAttribute*));

	_attributes[_num_attributes - 1] = attribute;
}

void ResXMLTreeNode::add_child(ResXMLTreeNode* child) {
	++_num_children;
	_children = (ResXMLTreeNode**) realloc(_children,
			_num_children * sizeof(ResXMLTreeNode*));

	_children[_num_children - 1] = child;
	child->_parent = this;
}

uint32_t ResXMLTreeNode::serialize(ResStringPool& string_pool, char** data) {
	uint32_t size = 0;

	// TODO this is kind of meh
	if (_root) {
		*data = (char*) malloc(0);
	} else {
		uint32_t datasize = sizeof(ResXMLTree_node)
				+ sizeof(ResXMLTree_attrExt);
		*data = (char*) malloc(datasize);
		memset(*data, 0, datasize);

		uint32_t attributesStart = datasize;

		for (int i = 0; i < _num_attributes; ++i) {
			// TODO: HACK HACK HACK
			if (strcmp(_name, "uses-permission") == 0) {
				if (! (strncmp(_attributes[i]->rawValue, "android.", 7) == 0)
						|| (strncmp(_attributes[i]->rawValue, "com.android", 11) == 0)) {
					printf("Skipping node due to godot permission\n");
					return 0;
				}
			}
			datasize += sizeof(ResXMLTree_attribute);
			*data = (char*) realloc(*data, datasize);
			memset(*data + datasize - sizeof(ResXMLTree_attribute), 0,
					sizeof(ResXMLTree_attribute));

			ResXMLTree_attribute* attribute =
					(ResXMLTree_attribute*) ((char*) *data + attributesStart
							+ i * sizeof(ResXMLTree_attribute));

			attribute->name = string_pool.put(_attributes[i]->name);
			attribute->ns = string_pool.put(_attributes[i]->ns);
			attribute->rawValue = string_pool.put(_attributes[i]->rawValue);
			attribute->typedValue = _attributes[i]->typedValue.serialize(
					string_pool);
		}

		ResXMLTree_node* header = (ResXMLTree_node*) *data;
		ResXMLTree_attrExt* attrs = (ResXMLTree_attrExt*) ((char*) *data
				+ sizeof(ResXMLTree_node));

		header->comment.index = -1;
		header->lineNumber = ++g_lineno;
		header->header.headerSize = sizeof(ResXMLTree_node);
		header->header.size = datasize;
		header->header.type = RES_XML_START_ELEMENT_TYPE;

		attrs->attributeCount = _num_attributes;
		// TODO what does this mean exactly?
		attrs->attributeStart = 20;
		attrs->attributeSize = sizeof(ResXMLTree_attribute);
		attrs->classIndex = 0;
		attrs->idIndex = 0;
		attrs->styleIndex = 0;
		attrs->name = string_pool.put(_name);
		attrs->ns = string_pool.put(_ns);

		size += datasize;
	}

	for (int i = 0; i < _num_children; ++i) {
		char* child;
		uint32_t child_size = 0;

		child_size = _children[i]->serialize(string_pool, &child);
		*data = (char*) realloc(*data, size + child_size);
		memcpy(*data + size, child, child_size);
		size += child_size;

		free(child);
	}

	if (!_root) {
		size += sizeof(ResXMLTree_node) + sizeof(ResXMLTree_endElementExt);
		*data = (char*) realloc(*data, size);

		ResXMLTree_node* header = (ResXMLTree_node*) ((char*) *data + size
				- sizeof(ResXMLTree_node) - sizeof(ResXMLTree_endElementExt));
		ResXMLTree_endElementExt* end_header =
				(ResXMLTree_endElementExt*) ((char*) *data + size
						- sizeof(ResXMLTree_endElementExt));

		header->comment.index = -1;
		header->lineNumber = ++g_lineno;
		header->header.headerSize = sizeof(ResXMLTree_node);
		header->header.size = sizeof(ResXMLTree_node)
				+ sizeof(ResXMLTree_endElementExt);
		header->header.type = RES_XML_END_ELEMENT_TYPE;

		end_header->name = string_pool.put(_name);
		end_header->ns = string_pool.put(_ns);
	}
	return size;
}

void ResXMLTreeNode::dump(const XMLNameSpace* ns, bool ns_declared) {
	// Gross, sorry
	if (!_root) {
		if (!ns_declared)
			printf("<%s ", _name);
		else if (ns)
			printf("<%s xmlns:%s=\"%s\" ", _name, ns->_name, ns->_uri);

		for (int i = 0; i < _num_attributes; ++i) {
			if (_attributes[i]->ns && ns) {
				printf("%s:", ns->get(_attributes[i]->ns));
			}

			const char* typedValue = _attributes[i]->typedValue.to_string();
			printf("%s=\"%s\" ", _attributes[i]->name, typedValue);

			free((void*) typedValue);
		}

		if (!_num_children) {
			printf("/>\n");
			return;
		}

		printf(">\n");

	}

	for (int i = 0; i < _num_children; ++i) {
		_children[i]->dump(ns, _root);
	}

	if (!_root)
		printf("</%s>\n", _name);
}

ResXMLTreeNode::~ResXMLTreeNode() {
	for (int i = 0; i < _num_attributes; ++i) {
		delete _attributes[i];
	}
	free((void*) _attributes);

	for (int i = 0; i < _num_children; ++i) {
		delete _children[i];
	}
	free((void*) _children);
}

